#include <cybergarage/x3d/SceneGraph.h>
#include "ply.h"
#include "importer_utility.h"

#include <GL/glew.h>
#include "Vector3.h"
#include "Matrix.h"
#include "Triangle.h"
#include "Utility.h"
#include <string>
#include <float.h>

#include "geometry.h"
#include <cuda_runtime.h>
#include <cutil.h>

#ifndef _WIN32
#define _strdup strdup
#endif

ModelInstance::ModelInstance()
{
	isInstanced = false;
	trilist = NULL;
	verlist = NULL;
	nVerts = 0;
	nTris = 0;
	bb[0] = Vector3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
	bb[1] = Vector3(FLT_MAX, FLT_MAX, FLT_MAX);
	vbo_Vertex = 0;
	vbo_TriIndex = 0;
	boundingBoxGenerated = false;
	obbTree = NULL;
	vertexPointer = NULL;
	triIdxPointer = NULL;
	bvh_constru = NULL;
}

ModelInstance::~ModelInstance()
{
	destroy();
}

void ModelInstance::destroy()
{
	if(!isInstanced)
	{
		if(verlist)
			free(verlist);
		verlist = NULL;
		
		if(trilist)
			delete [] trilist;
		trilist = NULL;
		
		if(vbo_TriIndex)
		{
			glDeleteBuffersARB(1, &vbo_TriIndex);
			vbo_TriIndex = 0;
		}
		
		if(vbo_Vertex)
		{
			glDeleteBuffersARB(1, &vbo_Vertex);
			vbo_Vertex = 0;
		}
		//
		if(obbTree)
		{
			CUDA_SAFE_CALL(cudaFree(obbTree));
			obbTree = NULL;
		}
	}
}

void ModelInstance::calculateAABoundingBox()
{
	if(!boundingBoxGenerated && nTris > 0)
	{
		bb[0] = Vector3(FLT_MAX, FLT_MAX, FLT_MAX);
		bb[1] = Vector3(-FLT_MAX, -FLT_MAX, -FLT_MAX);
		
		for(unsigned int i = 0; i < nTris; ++i)
		{
			const Triangle& t = trilist[i];
			for(unsigned int j = 0; j < 3; ++j)
			{
				int vid = t.p[j];
				const Vector3& v = verlist[vid];
				for(unsigned int k = 0; k < 3; ++k)
				{
					if(v[k] < bb[0][k])
						bb[0][k] = v[k];
					if(v[k] > bb[1][k])
						bb[1][k] = v[k];
				}
			}
		}
	}
}

int ModelInstance::load(const char* fileName, float scalarFactor)
{
	char* extension = _strdup(strrchr(fileName, '.'));
	strtolower(extension);
	
	int retCode;
	
	if(strcmp(extension, ".obj") == 0)
	{
		retCode = loadOBJ(fileName, scalarFactor);
	}
	else if(strcmp(extension, ".ply") == 0)
	{
		retCode = loadPLY(fileName, scalarFactor);
	}
	else if(strcmp(extension, ".tri") == 0)
	{
		retCode = loadTRI(fileName, scalarFactor);
	}
	else if(strcmp(extension, ".tr2") == 0)
	{
		retCode = loadTRI2(fileName, scalarFactor);
	}
	else if(strcmp(extension, ".wrl") == 0)
	{
		retCode = loadVRML(fileName, scalarFactor);
	}
	else if(strcmp(extension, ".iv") == 0)
	{
		retCode = loadIV(fileName, scalarFactor);
	}
	
	free(extension);
	
	return retCode;
}

int ModelInstance::loadOBJ(const char* fileName, float scalarFactor)
{
	FILE* objFileStream = fopen(fileName, "rb");
	if(!objFileStream) return -1;
	
	unsigned int curVert = 0;
	
#define OBJ_COORD_X 0
#define OBJ_COORD_Y 1
#define OBJ_COORD_Z 2
	
	char lineBuffer[2000];
	bool has_vertexNormals = false;
	bool has_textureCoords = false;
	
	std::vector<SSEVertex> tempVertexList;
	std::vector<Triangle> tempTriangleList;
	std::vector<Vector2> tempTexCoords;
	std::vector<SSEVertex> tempVertexNormals;
	
	while(fgets(lineBuffer, 2000, objFileStream))
	{
		char *firstToken = strtok(lineBuffer, "\r\n\t ");
		
		if(!firstToken || firstToken[0] == '#' || firstToken[0] == 0)
			continue;
			
		strtolower(firstToken);
		
		switch(firstToken[0])
		{
		case 'v': // new vertex
			if(firstToken[1] == 'n')
			{
				// vertex normal
				SSEVertex newVert;
				newVert.e[OBJ_COORD_X] = (float)atof(strtok(NULL, "\t "));
				newVert.e[OBJ_COORD_Y] = (float)atof(strtok(NULL, "\t "));
				newVert.e[OBJ_COORD_Z] = (float)atof(strtok(NULL, "\t "));
				tempVertexNormals.push_back(newVert);
				has_vertexNormals = true;
			}
			else if(firstToken[1] == 't')
			{
				// texture coordinate
				Vector2 newVert;
				newVert.e[0] = (float)atof(strtok(NULL, "\t "));
				newVert.e[1] = 1.0f - (float)atof(strtok(NULL, "\t "));
				tempTexCoords.push_back(newVert);
				has_textureCoords = true;
			}
			else
			{
				SSEVertex newVert;
				newVert.e[OBJ_COORD_X] = (float)atof(strtok(NULL, "\t "));
				newVert.e[OBJ_COORD_Y] = (float)atof(strtok(NULL, "\t "));
				newVert.e[OBJ_COORD_Z] = (float)atof(strtok(NULL, "\t "));
				newVert *= scalarFactor;
				tempVertexList.push_back(newVert);
			}
			break;
		case 'f': // new face
		{
			Triangle tri;
#define OBJ_MAXVERTSPERFACE 30
			char *vertData[OBJ_MAXVERTSPERFACE];
			int nFaceVerts = 0;
			
			while((vertData[nFaceVerts] = strtok(NULL, "\t \r\n")) != NULL)
				if(strlen(vertData[nFaceVerts]))
					nFaceVerts++;
					
			for(int t = 0; t < (nFaceVerts - 2); t++)
			{
				if(has_textureCoords == false && has_vertexNormals == false)
				{
					// vertex indices
					tri.p[0] = atoi(vertData[0]) - 1;
					tri.p[1] = atoi(vertData[t + 1]) - 1;
					tri.p[2] = atoi(vertData[t + 2]) - 1;
				}
				else
				{
					// in vID/vtID/vnID format
					const char *v1;
					
					for(int i = 0; i < 3; i++)
					{
						// vertex ID
						if(i == 0)
							v1 = vertData[0];
						else
							v1 = vertData[t + i];
							
						tri.p[i] = atoi(v1) - 1;
						
						// texture coordinate
						const char *v2 = strchr(v1 + 1, '/');
						
						if(v2)
						{
#ifdef _USE_TEXTURING
							if(has_textureCoords)
								tri.uv[i] = tempTexCoords[atoi(v2 + 1) -1];
#endif
								
							// vertex normal
							const char *v3 = strchr(v2 + 1, '/');
						}
					}
				}
				
				tri.updateFromVerticesNoReorder(tempVertexList[tri.p[0]],
				                                tempVertexList[tri.p[1]],
				                                tempVertexList[tri.p[2]]);
				tri.n = -tri.n;
				
#ifdef _USE_TEXTURING
				if(has_textureCoords)
				{
					tri.uv[1] = tri.uv[1] - tri.uv[0];
					tri.uv[2] = tri.uv[2] - tri.uv[0];
				}
				else
				{
					tri.uv[0] = tri.uv[2] = tri.uv[1] = Vector2(0, 0);
				}
#endif
				
				
				tempTriangleList.push_back(tri);
			}
		}
		break;
		case 'u': // use material
		case 's': // no idea
		case 'o': // new object
		case 'm': // material library
		case 'g': // scene name
			// do nothing
			break;
		default: // unknown tag:
			std::cout << "Warning: Unknown OBJ tag " << firstToken << " skipping." << std::endl;
		}
	}
	
	std::cout << "OBJ file "  << fileName << " loaded, " << tempTriangleList.size() << "faces, " << tempVertexList.size() << " vertices" << std::endl;
	
	// copy over temporary vertex list:
	modelFileName = std::string(fileName);
	nVerts = (unsigned int)tempVertexList.size();
	verlist = (SSEVertex *)malloc(sizeof(SSEVertex) * nVerts);
	for(unsigned int i = 0; i < nVerts; i++)
		verlist[i] = tempVertexList[i];
	tempVertexList.clear();
	
	// copy over temporary triangle list:
	nTris = (unsigned int)tempTriangleList.size();
	trilist = new Triangle[nTris];
	for(unsigned int i = 0; i < nTris; i++)
		trilist[i] = tempTriangleList[i];
	tempTriangleList.clear();
	
	return 1;
}


int ModelInstance::loadPLY(const char* fileName, float scalarFactor)
{
	// PLY object:
	PlyFile *ply;
	
	// PLY properties:
	char **elist;
	int nelems, num_elems;
	int file_type;
	float version;
	float plyScale = scalarFactor;
	
	// open a stream of the file given to us
	FILE *plyFileStream = fopen(fileName, "rb");
	
	if(!plyFileStream)
		return -1;
		
	// hand over the stream to the ply functions:
	ply = ply_read(plyFileStream, &nelems, &elist);
	
	if(ply == NULL)
	{
		printf("Error while parsing the PLY file!\n");
		return -1;
	}
	
	ply_get_info(ply, &version, &file_type);
	
	std::cout << "PLY file " << fileName << " loaded, version " << version << ", " << nelems << " elements" << std::endl;
	
	// buffer to store vertices in while parsing:
	unsigned int curVert = 0;
	
	typedef struct PLYVertex
	{
		float coords[3];
		unsigned char color[3];
		void *other_props;
	} PLYVertex;
	
	typedef struct PLYFace
	{
		unsigned char nverts;
		int *verts;
		void *other_props;
	} PLYFace;
	
	PlyProperty vert_props[] =
	{
		/* list of property information for a vertex */
		{"x", PLY_FLOAT, PLY_FLOAT, 0, 0, 0, 0, 0},
		{"y", PLY_FLOAT, PLY_FLOAT, 4, 0, 0, 0, 0},
		{"z", PLY_FLOAT, PLY_FLOAT, 8, 0, 0, 0, 0},
		{"red", PLY_UCHAR, PLY_UCHAR, (int)offsetof(PLYVertex, color[0]), 0, 0, 0, 0},
		{"green", PLY_UCHAR, PLY_UCHAR, (int)offsetof(PLYVertex, color[1]), 0, 0, 0, 0},
		{"blue", PLY_UCHAR, PLY_UCHAR, (int)offsetof(PLYVertex, color[2]), 0, 0, 0, 0},
	};
	
	PlyProperty face_props[] =
	{
		/* list of property information for a vertex */
		{"vertex_indices", PLY_INT, PLY_INT, offsetof(PLYFace, verts), 1, PLY_UCHAR, PLY_UCHAR, offsetof(PLYFace, nverts)},
	};
	
	// needed for parsing elements:
	char *elem_name;
	PlyProperty **plist;
	PlyOtherProp *vert_other = NULL, *face_other = NULL;
	PlyOtherElems *other_elements = NULL;
	bool has_vertex_indices = false;
	bool has_vertex_x = false, has_vertex_y = false, has_vertex_z = false, has_colors = false;
	
	unsigned char color_components = 0;
	unsigned char *colorBuffer;
	
	modelFileName = std::string(fileName);
	
	int nprops;
	for(int i = 0; i < nelems; i++)
	{
		// get the description of the first element (i.e. type)
		elem_name = elist[i];
		
		std::cout << "Element: " << i << " : " << elem_name << std::endl;
		
		plist = ply_get_element_description(ply, elem_name, &num_elems, &nprops);
		
		// this is a vertex:
		if(equal_strings("vertex", elem_name))
		{
			int j;
			for(j = 0; j < nprops; j++)
			{
				if(equal_strings("x", plist[j]->name))
				{
					ply_get_property(ply, elem_name, &vert_props[0]);   /* x */
					has_vertex_x = true;
				}
				else if(equal_strings("y", plist[j]->name))
				{
					ply_get_property(ply, elem_name, &vert_props[1]);   /* y */
					has_vertex_y = true;
				}
				else if(equal_strings("z", plist[j]->name))
				{
					ply_get_property(ply, elem_name, &vert_props[2]);   /* z */
					has_vertex_z = true;
				}
				else if(equal_strings("red", plist[j]->name))
				{
					ply_get_property(ply, elem_name, &vert_props[3]);   /* z */
					color_components++;
				}
				else if(equal_strings("green", plist[j]->name))
				{
					ply_get_property(ply, elem_name, &vert_props[4]);   /* z */
					color_components++;
				}
				else if(equal_strings("blue", plist[j]->name))
				{
					ply_get_property(ply, elem_name, &vert_props[5]);   /* z */
					color_components++;
				}
			}
			
			has_colors = color_components == 3;
			
			// test for necessary properties
			if((!has_vertex_x) || (!has_vertex_y) || (!has_vertex_z))
			{
				std::cout << "Warning: Vertex with less than 3 coordinated detected. Output will most likely be corrupt!" << std::endl;
				continue;
			}
			
			// grab all the vertex elements
			PLYVertex plyNewVertex;
			std::cout << "Allocating " << (num_elems * sizeof(SSEVertex)) << " bytes of storage." << std::endl;
			verlist = (SSEVertex *)malloc(sizeof(SSEVertex) * (num_elems + 1));
			nVerts = num_elems;
			
			// do we have vertex colors?
			if(has_colors)
			{
				colorBuffer = new unsigned char[num_elems*3];
				std::cout << "Allocating " << (num_elems * 3) << " bytes of storage for vertex colors." << std::endl;
			}
			
			for(j = 0; j < num_elems; j++)
			{
				ply_get_element(ply, (void *)&plyNewVertex);
				
				if(has_colors)
				{
					colorBuffer[curVert*3] = plyNewVertex.color[0];
					colorBuffer[curVert*3 + 1] = plyNewVertex.color[1];
					colorBuffer[curVert*3 + 2] = plyNewVertex.color[2];
				}
				
				verlist[curVert++] = (plyScale * Vector3(plyNewVertex.coords));
				
				if(j != 0 && j % 1000000 == 0)
				{
					std::cout << " - " << j << " of " << num_elems << " loaded." << std::endl;
				}
			}
		}
		// this is a face (and, hopefully, a triangle):
		else if(equal_strings("face", elem_name))
		{
			int j;
			for(j = 0; j < nprops; j++)
			{
				if(equal_strings("vertex_indices", plist[j]->name))
				{
					ply_get_property(ply, elem_name, &face_props[0]);   /* vertex_indices */
					has_vertex_indices = true;
				}
			}
			
			/* test for necessary properties */
			if(!has_vertex_indices)
			{
				std::cout << "Warning: Face without vertex indices detected in PLY file. Output will most likely be corrupt!" << std::endl;
				continue;
			}
			
			/* grab all the face elements */
			Triangle tri;
			PLYFace plyFace;
			plyFace.other_props = NULL;
			
			std::cout << "Allocating " << (num_elems * sizeof(Triangle)) << " bytes of storage." << std::endl;
			trilist = new Triangle[num_elems];
			
			for(j = 0; j < num_elems; j++)
			{
				ply_get_element(ply, (void *)&plyFace);
				if(plyFace.nverts != 3)
				{
					std::cout << "Warning: Face with more than 3 vertices detected. The importer will only read triangles, skipping..." << std::endl;
					continue;
				}
				
				//
				// make a triangle in our format from PLY face + vertices
				//
				
				
				// copy vertex indices
				tri.p[0] = plyFace.verts[0];
				tri.p[1] = plyFace.verts[1];
				tri.p[2] = plyFace.verts[2];
				
				tri.updateFromVerticesNoReorder(verlist[tri.p[0]],
				                                verlist[tri.p[1]],
				                                verlist[tri.p[2]]);
				                                
				// insert triangle into list
				trilist[nTris++] = tri;
				
				if(j != 0 && j % 500000 == 0)
				{
					std::cout << " - " << j << " of " << num_elems << " loaded." << std::endl;
				}
			}
			
		}
		else // otherwise: skip all further
			other_elements = ply_get_other_element(ply, elem_name, num_elems);
	}
	
	// PLY parsing ended, clean up vertex buffer and close the file
	ply_close(ply);
	fclose(plyFileStream);
	
	return 1;
}

int ModelInstance::loadTRI(const char* fileName, float scalarFactor)
{
	FILE *triFileStream = fopen(fileName, "rb");
	if(!triFileStream)
		return -1;
		
	char buffer[20];
	fscanf(triFileStream, "%s", buffer);
	assert(strcmp(buffer, "TRI") == 0);
	
	fscanf(triFileStream, "%d", &nVerts);
	fscanf(triFileStream, "%d", &nTris);
	
	float* vertices = new float[3 * nVerts];
	int* triIndex = new int[3 * nTris];
	
	for(unsigned int i = 0; i < 3 * nVerts; ++i)
	{
		float tmp;
		fscanf(triFileStream, "%f", &tmp);
		vertices[i] = tmp;
	}
	
	for(unsigned int i = 0; i < 3 * nTris; ++i)
	{
		int tmp;
		fscanf(triFileStream, "%d", &tmp);
		triIndex[i] = tmp;
	}
	
	modelFileName = std::string(fileName);
	
	
	
	verlist = (SSEVertex *)malloc(sizeof(SSEVertex) * nVerts);
	for(unsigned int i = 0; i < nVerts; i++)
	{
		verlist[i] = SSEVertex(vertices[3 * i], vertices[3 * i + 1], vertices[3 * i + 2]);
		verlist[i] *= scalarFactor;
	}
	
	trilist = new Triangle[nTris];
	for(unsigned int i = 0; i < nTris; i++)
	{
		Triangle tmp;
		tmp.p[0] = triIndex[3 * i];
		tmp.p[1] = triIndex[3 * i + 1];
		tmp.p[2] = triIndex[3 * i + 2];
		trilist[i] = tmp;
	}
	
	
	delete [] vertices;
	vertices = NULL;
	delete [] triIndex;
	triIndex = NULL;
	
	fclose(triFileStream);
	
	return 1;
}

int ModelInstance::loadTRI2(const char* fileName, float scalarFactor)
{
	FILE *tri2FileStream = fopen(fileName, "rb");
	if(!tri2FileStream)
		return -1;
		
	fscanf(tri2FileStream, "%d", &nTris);
	nVerts = 3 * nTris;
	
	float* vertices = new float[3 * nVerts];
	int* triIndex = new int[3 * nTris];
	
	
	for(unsigned int i = 0; i < 3 * nVerts; ++i)
	{
		float tmp;
		fscanf(tri2FileStream, "%f", &tmp);
		vertices[i] = tmp;
	}
	
	for(unsigned int i = 0; i < 3 * nTris; ++i)
	{
		triIndex[i] = i;
	}
	
	modelFileName = std::string(fileName);
	
	verlist = (SSEVertex *)malloc(sizeof(SSEVertex) * nVerts);
	for(unsigned int i = 0; i < nVerts; i++)
	{
		verlist[i] = SSEVertex(vertices[3 * i], vertices[3 * i + 1], vertices[3 * i + 2]);
		verlist[i] *= scalarFactor;
	}
	
	// copy over temporary triangle list:
	trilist = new Triangle[nTris];
	for(unsigned int i = 0; i < nTris; i++)
	{
		Triangle tmp;
		tmp.p[0] = triIndex[3 * i];
		tmp.p[1] = triIndex[3 * i + 1];
		tmp.p[2] = triIndex[3 * i + 2];
		trilist[i] = tmp;
	}
	
	delete [] vertices;
	vertices = NULL;
	delete [] triIndex;
	triIndex = NULL;
	
	fclose(tri2FileStream);
	
	return 1;
}

int ModelInstance::loadVRML(const char *fileName, float scalarFactor)
{
	CyberX3D::SceneGraph *sceneGraph;
	int retCode = 1;
	
	std::cout << "Loading VRML file " << fileName << "." << std::endl;
	
	// load VRML file
	char *vrmlFileName = _strdup(fileName);
	sceneGraph = new CyberX3D::SceneGraph();
	
	// loaded without errors ?
	if(sceneGraph->load(vrmlFileName))    // yes, get information from scene graph:
	{
		free(vrmlFileName);
		sceneGraph->initialize();
		std::cout << "parsing VRML scene graph..." << std::endl;
		traverseVRMLSceneGraph(sceneGraph, sceneGraph->getNodes(), 0);
		
		for(unsigned int i = 0; i < nVerts; i++)
		{
			verlist[i] *= scalarFactor;
		}
	}
	else   // no, error reading VRML file !
	{
		free(vrmlFileName);
		std::cout << "Error reading VRML file (line " << sceneGraph->getParserErrorLineNumber() << ") : " << sceneGraph->getParserErrorLineString() << std::endl;
		return -1;
	}
	
	return retCode;
}

void ModelInstance::traverseVRMLSceneGraph(CyberX3D::SceneGraph *sceneGraph, CyberX3D::Node *firstNode, unsigned int level)
{
	static TriangleList *tempTriList = NULL;
	static Vector3List *tempVectorList = NULL;
	
	if(!firstNode)
		return;
		
	if(tempTriList == NULL)
		tempTriList = new TriangleList;
	if(tempVectorList == NULL)
		tempVectorList = new Vector3List;
		
	CyberX3D::Node *node;
	
	// Traverse all nodes:
	for(node = firstNode; node; node = node->next())
	{
		if(node->isShapeNode())    // geometry node, read triangle information
		{
		
			CyberX3D::ShapeNode* shape = (CyberX3D::ShapeNode*)node;
			CyberX3D::GeometryNode* gnode = shape->getGeometry3D();
			CyberX3D::AppearanceNode* appearance = shape->getAppearanceNodes();
			float point[3];
			Triangle tri;
			Vector3 n;
			Vector3 normals[3];
			Vector2 uv[3];
			Matrix m, mRot;;
			
			// Transformation matrix of the following shape.
			// All coordinates (vertices and normals) need to
			// be Transformed with this so we get the proper
			// values
			shape->getTransformMatrix(m.x);
			m.transpose();
			
			// Generate copy of matrix without translation
			// for Transforming the normal vectors
			mRot = m;
			mRot.x[0][3] = 0;
			mRot.x[1][3] = 0;
			mRot.x[2][3] = 0;
			
			// Handle geometry types:
			if(gnode->isIndexedFaceSetNode())
			{
				CyberX3D::IndexedFaceSetNode *idxFaceSet = (CyberX3D::IndexedFaceSetNode *)gnode;
				int pointCount = 0;
				int coordIndex;
				
#define MAX_POLYGON_POINTS 12
				SSEVertex triPoints[MAX_POLYGON_POINTS];
				
				// Triangle coordinates
				CyberX3D::CoordinateNode *coordinateNode = idxFaceSet->getCoordinateNodes();
				if(!coordinateNode)
					continue;
					
				// Texture coordinates, if existing
				CyberX3D::TextureCoordinateNode *texCoordNode	= idxFaceSet->getTextureCoordinateNodes();
				
				// Vertex normals, if existing
				CyberX3D::NormalNode	*normalNode	= idxFaceSet->getNormalNodes();
				
				int nNormalIndexes = idxFaceSet->getNNormalIndexes();
				int nCoordIndexes = idxFaceSet->getNCoordIndexes();
				int nTexCoordIndexes = idxFaceSet->getNTexCoordIndexes();
				
				for(int nCoordIndex = 0; nCoordIndex < nCoordIndexes; nCoordIndex++)
				{
					coordIndex = idxFaceSet->getCoordIndex(nCoordIndex);
					
					// If index set ended *and* we have 3 points, finish off triangle
					if(coordIndex == -1)
					{
						//cout << "Creating tris from " << pointCount << " vertices." << endl;
						for(int triN = 0; triN < (pointCount - 2); triN++)  // triangulate polygon
						{
							// Finished with coordinates.
							
							// Calculate unit normal vector (check winding so it will
							// point the right way !)
							if(idxFaceSet->getCCW())
								n = cross(triPoints[triN + 1] - triPoints[triN + 0], triPoints[triN + 2] - triPoints[triN + 0]);
							else
								n = cross(triPoints[triN + 2] - triPoints[triN + 0], triPoints[triN + 1] - triPoints[triN + 0]);
								
							// Degenerate triangle, points must be collinear/identical:
							// skip this triangle
							if(n.squaredLength() == 0.0)
							{
								std::cout << "Degenerate triangle (collinear points?) detected!" << std::endl;
								pointCount = 0;
								continue;
							}
							
							n.makeUnitVector();
							
							// get triangle indices from best index combination:
							int v_offset = nVerts - 3 - ((pointCount - 3) - triN);
							tri.p[0] = v_offset;
							tri.p[1] = v_offset + 1;
							tri.p[2] = v_offset + 2;
							
							// insert triangle into list
							tempTriList->push_back(tri);
						}
						
						pointCount = 0;
					}
					else
					{
						coordinateNode->getPoint(coordIndex, point);
						
						if(pointCount >= MAX_POLYGON_POINTS)    // if we've got more than 3 points, ignore this
						{
							std::cout << "Surface with too many vertices detected, ignoring..." << std::endl;
							pointCount = 0;
						}
						
						// save point in triangle (Transformed by matrix)
						triPoints[pointCount] = SSEVertex(m * Vector3(point));
						tempVectorList->push_back(triPoints[pointCount]);
						nVerts++;
						
						pointCount++;
					}
				}
			}
			
			
		}
		else
			traverseVRMLSceneGraph(sceneGraph, node->getChildNodes(), level + 1);
	}
	
	// Traversal ended if this is the first level.
	// Now copy dynamically allocated lists to static arrays:
	//
	if(level == 0)
	{
		unsigned int i;
		
		// Allocate memory for tris:
		std::cout << tempVectorList->size() << " Vertices," << std::endl;
		std::cout << "Allocating " << (tempVectorList->size() * sizeof(Vector3)) << " Bytes." << std::endl;
		verlist = (SSEVertex *)malloc(sizeof(SSEVertex) * tempVectorList->size());
		
		// Allocate memory for tris:
		std::cout << tempTriList->size() << " Triangles," << std::endl;
		std::cout << "Allocating " << (tempTriList->size() * sizeof(Triangle)) << " Bytes." << std::endl;
		trilist = new Triangle[tempTriList->size()];
		
		//
		// Copy vertices from list to static array:
		//
		
		for(i = 0; i < tempVectorList->size(); i++)
			verlist[i] = tempVectorList->at(i);
			
		//
		// Copy triangles from list to static array:
		//
		
		for(i = 0; i < tempTriList->size(); i++)
		{
			trilist[nTris] = tempTriList->at(i);
			nTris++;
		}
		
		if(NULL != tempTriList)
		{
			delete tempTriList;
			tempTriList = NULL;
		}
		
		if(NULL != tempVectorList)
		{
			delete tempVectorList;
			tempVectorList = NULL;
		}
	}
}


int ModelInstance::loadIV(const char* fileName, float scalarFactor)
{
#define FACE_MAX 200000
#define ORDER_MAX 10
#define TEXTURE_MAX 100
#define LINES_MAX 100000
#define COLOR_MAX 1000
#define LEVEL_MAX 10
	FILE* ivFileStream = fopen(fileName, "rb");
	if (!ivFileStream) return -1;
	
	modelFileName = std::string(fileName);
	
	char input[256];
	
	
	int face_num = 0;
	float cor3[3][COR3_MAX];
	int face[10][COR3_MAX];
	int face_order[COR3_MAX];
	int cor3_num = 0;
	int color_num = 0;
	int bad_num = 0;
	int texture_num = 0;
	int text_num = 0;
	int line_num = 0;
	int comment_num = 0;
	int debug;
	float face_normal[3][FACE_MAX];
	char   normal_binding[80];
	float  normal_temp[3][ORDER_MAX*FACE_MAX];
	float  vertex_normal[3][ORDER_MAX][FACE_MAX];
	char   texture_name[TEXTURE_MAX][256];
	float  texture_temp[2][ORDER_MAX*FACE_MAX];
	char   texture_binding[80];
	float  rgbcolor[3][COLOR_MAX];
	int    line_material[LINES_MAX];
	float  vertex_rgb[3][ORDER_MAX][FACE_MAX];
	float  vertex_tex_uv[2][ORDER_MAX][FACE_MAX];
	char   material_binding[80];
	int    vertex_material[ORDER_MAX][FACE_MAX];
	float  transform_matrix[4][4];
	int    line_dex[LINES_MAX];
	char   level_name[LEVEL_MAX][256];
	
	char  c;
	int   count;
	int   i;
	int   icol;
	int   icolor;
	int   icface;
	int   inormface;
	int   iface_num;
	int   irow;
	int   iuv;
	int   ivert;
	int   iword;
	int   ix;
	int   ixyz;
	int   iy;
	int   iz;
	int   j;
	int   jval;
	int   level;
	char *next;
	int   nlbrack;
	int   nrbrack;
	int   nu;
	int   null_index;
	int   cor3_num_old;
	int   line_num2;
	int   face_num2;
	int   normal_num_temp;
	int   text_numure_temp;
	int   nv;
	int   result;
	float rval;
	int   width;
	char  word[256];
	char  word1[256];
	char  wordm1[256];
	float xvec[3];
	
	icface = 0;
	icol = -1;
	inormface = 0;
	iface_num = face_num;
	irow = 0;
	ix = 0;
	ixyz = 0;
	iy = 0;
	iz = 0;
	jval = 0;
	level = 0;
	strcpy(level_name[0], "Top");
	nlbrack = 0;
	nrbrack = 0;
	nu = 0;
	cor3_num_old = cor3_num;
	face_num2 = face_num;
	line_num2 = line_num;
	normal_num_temp = 0;
	text_numure_temp = 0;
	nv = 0;
	rval = 0.0;
	strcpy(word, " ");
	strcpy(wordm1, " ");
	
	
	/*
	  Read the next line of text from the input file.
	*/
	for (;;)
	{
	
		if (fgets(input, 256, ivFileStream) == NULL)
		{
			break;
		}
		
		text_num = text_num + 1;
		next = input;
		iword = 0;
		/*
		  Remove all commas from the line, so we can use SSCANF to read
		  numeric items.
		*/
		i = 0;
		while (input[i] != '\0')
		{
			if (input[i] == ',')
			{
				input[i] = ' ';
			}
			i++;
		}
		/*
		  Force brackets and braces to be buffered by spaces.
		*/
		i = 0;
		while (input[i] != '\0')
		{
			i++;
		}
		null_index = i;
		
		i = 0;
		while (input[i] != '\0' && i < 256)
		{
		
			if (input[i] == '[' || input[i] == ']' ||
			        input[i] == '{' || input[i] == '}')
			{
			
				result = importer_char_pad(&i, &null_index, input, 256);
				if (result == false)
				{
					break;
				}
			}
			else
			{
				i++;
			}
		}
		/*
		  Read a word from the line.
		*/
		for (;;)
		{
		
			strcpy(wordm1, word);
			strcpy(word, " ");
			
			count = sscanf(next, "%s%n", word, &width);
			next = next + width;
			
			if (count <= 0)
			{
				break;
			}
			
			iword = iword + 1;
			
			if (iword == 1)
			{
				strcpy(word1, word);
			}
			/*
			  The first line of the file must be the header.
			*/
			if (text_num == 1)
			{
			
				if (importer_equal_strings(word1, "#Inventor") != true)
				{
					printf("\n");
					printf("IV_READ - Fatal error!\n");
					printf("  The input file has a bad header.\n");
					return false;
				}
				else
				{
					comment_num = comment_num + 1;
				}
				break;
			}
			/*
			  A comment begins anywhere with '#'.
			  Skip the rest of the line.
			*/
			if (word[1] == '#')
			{
				comment_num = comment_num + 1;
				break;
			}
			/*
			  If the word is a curly or square bracket, count it.
			  If the word is a left bracket, the previous word is the name of a node.
			*/
			if (strcmp(word, "{") == 0 || strcmp(word, "[") == 0)
			{
				nlbrack = nlbrack + 1;
				level = nlbrack - nrbrack;
				strcpy(level_name[level], wordm1);
				if (debug)
				{
					printf("Begin level: %s\n", wordm1);
				}
			}
			else if (strcmp(word, "}") == 0 || strcmp(word, "]") == 0)
			{
				nrbrack = nrbrack + 1;
				
				if (nlbrack < nrbrack)
				{
					printf("\n");
					printf("IV_READ - Fatal error!\n");
					printf("  Extraneous right bracket on line %d.\n", text_num);
					printf("  Currently processing field %s\n.", level_name[level]);
					return false;
				}
			}
			/*
			  BASECOLOR
			*/
			if (importer_equal_strings(level_name[level], "BASECOLOR") == true)
			{
			
				if (strcmp(word, "{") == 0)
				{
				}
				else if (strcmp(word, "}") == 0)
				{
					level = nlbrack - nrbrack;
				}
				else if (importer_equal_strings(word, "RGB") == true)
				{
				}
				else
				{
					bad_num = bad_num + 1;
					printf("Bad data %s\n", word);
				}
			}
			/*
			  COORDINATE3
			*/
			else if (importer_equal_strings(level_name[level], "COORDINATE3") == true)
			{
			
				if (strcmp(word, "{") == 0)
				{
				}
				else if (strcmp(word, "}") == 0)
				{
					level = nlbrack - nrbrack;
				}
				else if (importer_equal_strings(word, "POINT") == true)
				{
				}
				else
				{
					bad_num = bad_num + 1;
					printf("COORDINATE3: Bad data %s\n", word);
				}
			}
			/*
			  COORDINATE4
			*/
			else if (importer_equal_strings(level_name[level], "COORDINATE4") == true)
			{
			
				if (strcmp(word, "{") == 0)
				{
				}
				else if (strcmp(word, "}") == 0)
				{
					level = nlbrack - nrbrack;
				}
				else if (importer_equal_strings(word, "POINT") == true)
				{
				}
				else
				{
					bad_num = bad_num + 1;
					printf("COORDINATE4: Bad data %s\n", word);
				}
			}
			/*
			  COORDINDEX
			*/
			else if (importer_equal_strings(level_name[level], "COORDINDEX") == true)
			{
			
				if (strcmp(word, "[") == 0)
				{
					ivert = 0;
				}
				else if (strcmp(word, "]") == 0)
				{
					level = nlbrack - nrbrack;
				}
				/*
				  (indexedlineset) COORDINDEX
				*/
				else if (importer_equal_strings(level_name[level-1], "INDEXEDLINESET") == true)
				{
				
					count = sscanf(word, "%d%n", &jval, &width);
					
					if (count > 0)
					{
					
						if (jval < -1)
						{
							bad_num = bad_num + 1;
						}
						else
						{
							if (line_num < LINES_MAX)
							{
								if (jval != -1)
								{
									jval = jval + cor3_num_old;
								}
								line_dex[line_num] = jval;
							}
							line_num = line_num + 1;
						}
					}
					else
					{
						bad_num = bad_num + 1;
					}
				}
				/*
				  (indexedfaceset) COORDINDEX
				  Warning: If the list of indices is not terminated with a final -1, then
				  the last face won't get counted.
				*/
				else if (importer_equal_strings(level_name[level-1], "INDEXEDFACESET") == true)
				{
				
					count = sscanf(word, "%d%n", &jval, &width);
					
					if (count > 0)
					{
						if (jval == -1)
						{
							ivert = 0;
							face_num = face_num + 1;
						}
						else
						{
							if (ivert == 0)
							{
								if (face_num < FACE_MAX)
								{
									face_order[face_num] = 0;
								}
							}
							if (face_num < FACE_MAX)
							{
								face_order[face_num] = face_order[face_num] + 1;
								face[ivert][face_num] = jval + cor3_num_old;
								ivert = ivert + 1;
							}
						}
					}
				}
				/*
				  (indexednurbssurface) COORDINDEX
				*/
				else if (importer_equal_strings(level_name[level-1], "INDEXEDNURBSSURFACE") == true)
				{
				}
				/*
				  (indexedtrianglestripset) COORDINDEX
				
				  First three coordinate indices I1, I2, I3 define a triangle.
				  Next triangle is defined by I2, I3, I4 (actually, I4, I3, I2
				  to stay with same counterclockwise sense).
				  Next triangle is defined by I3, I4, I5 ( do not need to reverse
				  odd numbered triangles) and so on.
				  List is terminated with -1.
				*/
				else if (importer_equal_strings(level_name[level-1], "INDEXEDTRIANGLESTRIPSET") == true)
				{
				
					count = sscanf(word, "%d%n", &jval, &width);
					
					if (count > 0)
					{
					
						if (jval == -1)
						{
							ivert = 0;
						}
						else
						{
						
							ix = iy;
							iy = iz;
							iz = jval + cor3_num_old;
							
							if (ivert == 0)
							{
								if (face_num < FACE_MAX)
								{
									face[ivert][face_num] = jval + cor3_num_old;
									face_order[face_num] = 3;
								}
							}
							else if (ivert == 1)
							{
								if (face_num < FACE_MAX)
								{
									face[ivert][face_num] = jval + cor3_num_old;
								}
							}
							else if (ivert == 2)
							{
								if (face_num < FACE_MAX)
								{
									face[ivert][face_num] = jval + cor3_num_old;
								}
								face_num = face_num + 1;
								
							}
							else
							{
							
								if (face_num < FACE_MAX)
								{
									face_order[face_num] = 3;
									if ((ivert % 2) == 0)
									{
										face[0][face_num] = ix;
										face[1][face_num] = iy;
										face[2][face_num] = iz;
									}
									else
									{
										face[0][face_num] = iz;
										face[1][face_num] = iy;
										face[2][face_num] = ix;
									}
								}
								face_num = face_num + 1;
							}
							ivert = ivert + 1;
							/*
							  Very very tentative guess as to how indices into the normal
							  vector array are set up...
							*/
							if (face_num < FACE_MAX && ivert > 2)
							{
								for (i = 0; i < 3; i++)
								{
									face_normal[i][face_num] = normal_temp[i][ix];
								}
							}
						}
					}
				}
			}
			/*
			  INDEXEDFACESET
			*/
			else if (importer_equal_strings(level_name[level], "INDEXEDFACESET") == true)
			{
			
				if (strcmp(word, "{") == 0)
				{
				}
				else if (strcmp(word, "}") == 0)
				{
					level = nlbrack - nrbrack;
				}
				else if (importer_equal_strings(word, "COORDINDEX") == true)
				{
					ivert = 0;
				}
				else if (importer_equal_strings(word, "MATERIALINDEX") == true)
				{
				}
				else if (importer_equal_strings(word, "NORMALINDEX") == true)
				{
				}
				else if (importer_equal_strings(word, "TEXTURECOORDINDEX") == true)
				{
					if (texture_num <= 0)
					{
						texture_num = 1;
						strcpy(texture_name[0], "Texture_0000");
					}
				}
				else
				{
					bad_num = bad_num + 1;
					printf("Bad data %s\n", word);
				}
			}
			/*
			  INDEXEDLINESET
			*/
			else if (importer_equal_strings(level_name[level], "INDEXEDLINESET") == true)
			{
			
				if (strcmp(word, "{") == 0)
				{
				}
				else if (strcmp(word, "}") == 0)
				{
					level = nlbrack - nrbrack;
				}
				else if (importer_equal_strings(word, "COORDINDEX") == true)
				{
				}
				else if (importer_equal_strings(word, "MATERIALINDEX") == true)
				{
				}
				else
				{
					bad_num = bad_num + 1;
					printf("Bad data %s\n", word);
				}
			}
			/*
			  INDEXEDNURBSSURFACE
			*/
			else if (importer_equal_strings(level_name[level], "INDEXEDNURBSSURFACE") == true)
			{
			
				if (strcmp(word, "{") == 0)
				{
				}
				else if (strcmp(word, "}") == 0)
				{
					level = nlbrack - nrbrack;
				}
				else if (importer_equal_strings(word, "NUMUCONTROLPOINTS") == true)
				{
				
					count = sscanf(word, "%d%n", &jval, &width);
					
					if (count > 0)
					{
						nu = jval;
						if (debug)
						{
							printf("NU = %d\n", nu);
						}
					}
					else
					{
						nu = 0;
						bad_num = bad_num + 1;
						printf("Bad data %s\n", word);
					}
				}
				else if (importer_equal_strings(word, "NUMVCONTROLPOINTS") == true)
				{
				
					count = sscanf(word, "%d%n", &jval, &width);
					
					if (count > 0)
					{
						nv = jval;
						if (debug)
						{
							printf("NV = %d\n", nv);
						}
					}
					else
					{
						nv = 0;
						bad_num = bad_num + 1;
					}
				}
				else if (importer_equal_strings(word, "COORDINDEX") == true)
				{
				}
				else if (importer_equal_strings(word, "UKNOTVECTOR") == true)
				{
				}
				else if (importer_equal_strings(word, "VKNOTVECTOR") == true)
				{
				}
				else
				{
					bad_num = bad_num + 1;
					printf("Bad data %s\n", word);
				}
			}
			/*
			  INDEXEDTRIANGLESTRIPSET
			*/
			else if (importer_equal_strings(level_name[level], "INDEXEDTRIANGLESTRIPSET") == true)
			{
			
				if (strcmp(word, "{") == 0)
				{
				}
				else if (strcmp(word, "}") == 0)
				{
					level = nlbrack - nrbrack;
				}
				else if (importer_equal_strings(word, "VERTEXPROPERTY") == true)
				{
					count = sscanf(next, "%s%n", word, &width);
					next = next + width;
				}
				else if (importer_equal_strings(word, "COORDINDEX") == true)
				{
					ivert = 0;
				}
				else if (importer_equal_strings(word, "NORMALINDEX") == true)
				{
					count = sscanf(next, "%s%n", word, &width);
					next = next + width;
				}
				else
				{
					bad_num = bad_num + 1;
					printf("Bad data %s\n", word);
				}
			}
			/*
			  INFO
			*/
			else if (importer_equal_strings(level_name[level], "INFO") == true)
			{
			
				if (strcmp(word, "{") == 0)
				{
				}
				else if (strcmp(word, "}") == 0)
				{
					level = nlbrack - nrbrack;
				}
				else if (importer_equal_strings(word, "STRING") == true)
				{
				}
				else if (strcmp(word, "\"") == 0)
				{
				}
				else
				{
				}
			}
			/*
			  LIGHTMODEL
			  Read, but ignore.
			*/
			else if (importer_equal_strings(level_name[level], "LIGHTMODEL") == true)
			{
			
				if (strcmp(word, "{") == 0)
				{
				}
				else if (strcmp(word, "}") == 0)
				{
					level = nlbrack - nrbrack;
				}
				else if (importer_equal_strings(word, "model") == true)
				{
				}
				else
				{
				}
			}
			/*
			  MATERIAL
			  Read, but ignore.
			*/
			else if (importer_equal_strings(level_name[level], "MATERIAL") == true)
			{
			
				if (strcmp(word, "{") == 0)
				{
				}
				else if (strcmp(word, "}") == 0)
				{
					level = nlbrack - nrbrack;
				}
				else if (importer_equal_strings(word, "AMBIENTCOLOR") == true)
				{
				}
				else if (importer_equal_strings(word, "EMISSIVECOLOR") == true)
				{
				}
				else if (importer_equal_strings(word, "DIFFUSECOLOR") == true)
				{
				}
				else if (importer_equal_strings(word, "SHININESS") == true)
				{
				}
				else if (importer_equal_strings(word, "SPECULARCOLOR") == true)
				{
				}
				else if (importer_equal_strings(word, "TRANSPARENCY") == true)
				{
				}
				else
				{
				}
			}
			/*
			  MATERIALBINDING
			  Read, but ignore
			*/
			else if (importer_equal_strings(level_name[level], "MATERIALBINDING") == true)
			{
			
				if (strcmp(word, "{") == 0)
				{
				}
				else if (strcmp(word, "}") == 0)
				{
					level = nlbrack - nrbrack;
				}
				else if (importer_equal_strings(word, "VALUE") == true)
				{
					count = sscanf(next, "%s%n", material_binding, &width);
					next = next + width;
				}
				else
				{
					count = sscanf(next, "%f%n", &rval, &width);
					next = next + width;
					
					if (count > 0)
					{
					}
					else
					{
						bad_num = bad_num + 1;
						printf("Bad data %s\n", word);
					}
				}
			}
			/*
			  MATERIALINDEX
			*/
			else if (importer_equal_strings(level_name[level], "MATERIALINDEX") == true)
			{
			
				if (strcmp(word, "[") == 0)
				{
					ivert = 0;
				}
				else if (strcmp(word, "]") == 0)
				{
					level = nlbrack - nrbrack;
				}
				/*
				  (indexedfaceset) MATERIALINDEX
				*/
				else if (importer_equal_strings(level_name[level-1], "INDEXEDFACESET") == true)
				{
				
					count = sscanf(word, "%d%n", &jval, &width);
					
					if (count > 0)
					{
					
						if (jval == -1)
						{
							ivert = 0;
							face_num2 = face_num2 + 1;
						}
						else
						{
						
							if (face_num2 < FACE_MAX)
							{
								if (jval != -1)
								{
									jval = jval + cor3_num_old;
								}
								vertex_material[ivert][face_num2] = jval;
								ivert = ivert + 1;
							}
						}
					}
					else
					{
						bad_num = bad_num + 1;
						printf("Bad data %s\n", word);
					}
				}
				/*
				  (indexedlineset) MATERIALINDEX
				*/
				else if (importer_equal_strings(level_name[level-1], "INDEXEDLINESET") == true)
				{
				
					count = sscanf(word, "%d%n", &jval, &width);
					
					if (count > 0)
					{
					
						if (line_num2 < LINES_MAX)
						{
							if (jval != -1)
							{
								jval = jval + cor3_num_old;
							}
							line_material[line_num2] = jval;
							line_num2 = line_num2 + 1;
						}
					}
					else
					{
						bad_num = bad_num + 1;
						printf("Bad data %s\n", word);
					}
				}
				else
				{
					count = sscanf(word, "%d%n", &jval, &width);
					
					
					if (count > 0)
					{
					}
					else
					{
						bad_num = bad_num + 1;
						printf("Bad data %s\n", word);
					}
				}
			}
			/*
			  MATRIXTRANSFORM.
			*/
			else if (importer_equal_strings(level_name[level], "MATRIXTRANSFORM") == true)
			{
			
				if (strcmp(word, "{") == 0)
				{
				}
				else if (strcmp(word, "}") == 0)
				{
					level = nlbrack - nrbrack;
				}
				else if (importer_equal_strings(word, "MATRIX") == true)
				{
					icol = -1;
					irow = 0;
				}
				else
				{
				
					count = sscanf(word, "%f%n", &rval, &width);
					
					if (count > 0)
					{
					
						icol = icol + 1;
						if (icol > 3)
						{
							icol = 0;
							irow = irow + 1;
							if (irow > 3)
							{
								irow = 0;
							}
						}
						
						transform_matrix[irow][icol] = rval;
					}
					
				}
			}
			/*
			  NORMAL
			  The field "VECTOR" may be followed by three numbers,
			  (handled here),  or by a square bracket, and sets of three numbers.
			*/
			else if (importer_equal_strings(level_name[level], "NORMAL") == true)
			{
				/*
				  (vertexproperty) NORMAL
				*/
				if (importer_equal_strings(level_name[level-1], "VERTEXPROPERTY") == true)
				{
				
					if (strcmp(word, "[") == 0)
					{
						ixyz = 0;
					}
					else if (strcmp(word, "]") == 0)
					{
						level = nlbrack - nrbrack;
					}
					else
					{
					
						count = sscanf(word, "%f%n", &rval, &width);
						
						if (count > 0)
						{
						
							if (inormface < FACE_MAX)
							{
								face_normal[ixyz][inormface] = rval;
							}
							
							ixyz = ixyz + 1;
							if (ixyz > 2)
							{
								ixyz = 0;
								inormface = inormface + 1;
							}
						}
					}
				}
				/*
				  (anythingelse) NORMAL
				*/
				else
				{
				
					if (strcmp(word, "{") == 0)
					{
						ixyz = 0;
					}
					else if (strcmp(word, "}") == 0)
					{
						level = nlbrack - nrbrack;
					}
					else if (importer_equal_strings(word, "VECTOR") == true)
					{
					}
					else
					{
					
						count = sscanf(word, "%f%n", &rval, &width);
						
						if (count > 0)
						{
						
							/*  COMMENTED OUT
							
							              if ( nfnorm < FACE_MAX ) {
							                normal[ixyz][nfnorm] = rval;
							              }
							
							*/
							ixyz = ixyz + 1;
							if (ixyz > 2)
							{
								ixyz = 0;
							}
						}
						else
						{
							bad_num = bad_num + 1;
							printf("Bad data %s\n", word);
						}
					}
				}
			}
			/*
			  NORMALBINDING
			  Read, but ignore
			*/
			else if (importer_equal_strings(level_name[level], "NORMALBINDING") == true)
			{
			
				if (strcmp(word, "{") == 0)
				{
				}
				else if (strcmp(word, "}") == 0)
				{
					level = nlbrack - nrbrack;
				}
				else if (importer_equal_strings(word, "VALUE") == true)
				{
					count = sscanf(next, "%s%n", normal_binding, &width);
					next = next + width;
				}
				else
				{
					count = sscanf(word, "%f%n", &rval, &width);
					
					if (count > 0)
					{
					}
					else
					{
						bad_num = bad_num + 1;
						printf("Bad data %s\n", word);
					}
				}
			}
			/*
			  NORMALINDEX
			*/
			else if (importer_equal_strings(level_name[level], "NORMALINDEX") == true)
			{
				/*
				  (indexedtrianglestripset) NORMALINDEX
				*/
				if (importer_equal_strings(level_name[level-1], "INDEXEDTRIANGLESTRIPSET") == true)
				{
					count = sscanf(word, "%d%n", &jval, &width);
					
					if (count > 0)
					{
					}
					else if (strcmp(word, "[") == 0)
					{
					}
					else if (strcmp(word, "]") == 0)
					{
					}
				}
				/*
				  (anythingelse) NORMALINDEX
				*/
				else
				{
				
					if (strcmp(word, "[") == 0)
					{
						ivert = 0;
					}
					else if (strcmp(word, "]") == 0)
					{
						level = nlbrack - nrbrack;
					}
					else
					{
					
						count = sscanf(word, "%d%n", &jval, &width);
						
						if (count > 0)
						{
							if (jval == -1)
							{
								ivert = 0;
								iface_num = iface_num + 1;
							}
							else
							{
								if (iface_num < FACE_MAX)
								{
									for (i = 0; i < 3; i++)
									{
										vertex_normal[i][ivert][iface_num] = normal_temp[i][jval];
									}
									ivert = ivert + 1;
								}
							}
						}
						else
						{
							bad_num = bad_num + 1;
							printf("Bad data %s\n", word);
						}
					}
				}
			}
			/*
			  (coordinate3) POINT
			*/
			else if (importer_equal_strings(level_name[level], "POINT") == true)
			{
			
				if (importer_equal_strings(level_name[level-1], "COORDINATE3") == true)
				{
				
					if (strcmp(word, "[") == 0)
					{
						ixyz = 0;
						cor3_num_old = cor3_num;
					}
					else if (strcmp(word, "]") == 0)
					{
						level = nlbrack - nrbrack;
					}
					else
					{
					
						count = sscanf(word, "%f%n", &rval, &width);
						
						if (count > 0)
						{
						
							if (cor3_num < COR3_MAX)
							{
								xvec[ixyz] = rval;
							}
							
							ixyz = ixyz + 1;
							
							if (ixyz == 3)
							{
							
								ixyz = 0;
								
								tmat_mxp(transform_matrix, xvec, xvec);
								
								cor3[0][cor3_num] = xvec[0];
								cor3[1][cor3_num] = xvec[1];
								cor3[2][cor3_num] = xvec[2];
								
								cor3_num = cor3_num + 1;
								
								continue;
							}
						}
						else
						{
							bad_num = bad_num + 1;
							break;
						}
					}
				}
				/*
				  (texturecoodinate2) POINT
				*/
				else if (importer_equal_strings(level_name[level-1], "TEXTURECOORDINATE2") == true)
				{
				
					if (strcmp(word, "[") == 0)
					{
						iuv = 0;
						text_numure_temp = 0;
					}
					else if (strcmp(word, "]") == 0)
					{
						level = nlbrack - nrbrack;
					}
					else
					{
					
						count = sscanf(word, "%f%n", &rval, &width);
						
						if (count > 0)
						{
						
							texture_temp[iuv][text_numure_temp] = rval;
							
							iuv = iuv + 1;
							if (iuv == 2)
							{
								iuv = 0;
								text_numure_temp = text_numure_temp + 1;
							}
						}
						else
						{
							printf("TextureCoordinate2 { Point [: Bad data\n");
							bad_num = bad_num + 1;
							break;
						}
					}
				}
			}
			/*
			  RGB
			*/
			else if (importer_equal_strings(level_name[level], "RGB") == true)
			{
				/*
				  (basecolor) RGB
				*/
				if (importer_equal_strings(level_name[level-1], "BASECOLOR") == true)
				{
				
					if (strcmp(word, "[") == 0)
					{
						icolor = 0;
					}
					else if (strcmp(word, "]") == 0)
					{
						level = nlbrack - nrbrack;
					}
					else
					{
					
						count = sscanf(word, "%f%n", &rval, &width);
						
						if (count > 0)
						{
						
							rgbcolor[icolor][color_num] = rval;
							icolor = icolor + 1;
							
							if (icolor == 3)
							{
								icolor = 0;
								color_num = color_num + 1;
							}
						}
						else
						{
							bad_num = bad_num + 1;
							printf("Bad data %s\n", word);
						}
					}
				}
				/*
				  (anythingelse RGB)
				*/
				else
				{
				
					printf("HALSBAND DES TODES!\n");
					
					if (strcmp(word, "[") == 0)
					{
						icolor = 0;
						ivert = 0;
					}
					else if (strcmp(word, "]") == 0)
					{
						level = nlbrack - nrbrack;
					}
					else
					{
					
						count = sscanf(word, "%f%n", &rval, &width);
						
						if (count > 0)
						{
						
							if (icface < FACE_MAX)
							{
							
								vertex_rgb[icolor][ivert][icface] = rval;
								
								icolor = icolor + 1;
								if (icolor == 3)
								{
									icolor = 0;
									color_num = color_num + 1;
									ivert = ivert + 1;
									if (ivert == face_order[icface])
									{
										ivert = 0;
										icface = icface + 1;
									}
								}
							}
						}
						else
						{
							bad_num = bad_num + 1;
							printf("Bad data %s\n", word);
						}
					}
				}
				
			}
			/*
			  SEPARATOR
			*/
			else if (importer_equal_strings(level_name[level], "SEPARATOR") == true)
			{
			
				if (strcmp(word, "{") == 0)
				{
				}
				else if (strcmp(word, "}") == 0)
				{
					level = nlbrack - nrbrack;
				}
				else
				{
				}
			}
			/*
			  SHAPEHINTS
			  Read, but ignore.
			*/
			else if (importer_equal_strings(level_name[level], "SHAPEHINTS") == true)
			{
			
				if (strcmp(word, "{") == 0)
				{
				}
				else if (strcmp(word, "}") == 0)
				{
					level = nlbrack - nrbrack;
				}
				else if (importer_equal_strings(word, "CREASEANGLE") == true)
				{
				
					count = sscanf(next, "%f%n", &rval, &width);
					next = next + width;
					
					if (count <= 0)
					{
						bad_num = bad_num + 1;
						printf("Bad data %s\n", word);
					}
				}
				else if (importer_equal_strings(word, "FACETYPE") == true)
				{
					count = sscanf(next, "%s%n", word, &width);
					next = next + width;
				}
				else if (importer_equal_strings(word, "SHAPETYPE") == true)
				{
					count = sscanf(next, "%s%n", word, &width);
					next = next + width;
				}
				else if (importer_equal_strings(word, "VERTEXORDERING") == true)
				{
					count = sscanf(next, "%s%n", word, &width);
					next = next + width;
				}
				else
				{
					bad_num = bad_num + 1;
					printf("Bad data %s\n", word);
				}
			}
			/*
			  TEXTURE2
			*/
			else if (importer_equal_strings(level_name[level], "TEXTURE2") == true)
			{
			
				if (strcmp(word, "{") == 0)
				{
				}
				else if (strcmp(word, "}") == 0)
				{
					level = nlbrack - nrbrack;
					texture_num = texture_num + 1;
				}
				else if (importer_equal_strings(word, "BLENDCOLOR") == true)
				{
				}
				/*
				  NEED TO REMOVE QUOTES SURROUNDING TEXTURE NAME.
				*/
				else if (importer_equal_strings(word, "FILENAME") == true)
				{
				
					count = sscanf(next, "%s%n", word, &width);
					next = next + width;
					
					strcpy(texture_name[texture_num], word);
					
					i = 0;
					j = 0;
					do
					{
						c = texture_name[texture_num][i];
						i = i + 1;
						if (c != '"')
						{
							texture_name[texture_num][j] = c;
							j = j + 1;
						}
					}
					while (c != '\0');
					
				}
				else if (importer_equal_strings(word, "IMAGE") == true)
				{
				}
				else if (importer_equal_strings(word, "MODEL") == true)
				{
					count = sscanf(next, "%s%n", word, &width);
					next = next + width;
				}
				else if (importer_equal_strings(word, "WRAPS") == true)
				{
					count = sscanf(next, "%s%n", word, &width);
					next = next + width;
				}
				else if (importer_equal_strings(word, "WRAPT") == true)
				{
					count = sscanf(next, "%s%n", word, &width);
					next = next + width;
				}
				else
				{
				}
			}
			/*
			  TEXTURECOORDINATE2
			*/
			else if (importer_equal_strings(level_name[level], "TEXTURECOORDINATE2") == true)
			{
			
				if (strcmp(word, "{") == 0)
				{
				}
				else if (strcmp(word, "}") == 0)
				{
					level = nlbrack - nrbrack;
				}
				else if (importer_equal_strings(word, "POINT") == true)
				{
				}
				else
				{
					bad_num = bad_num + 1;
					printf("TEXTURECOORDINATE2: Bad data %s\n", word);
				}
			}
			/*
			  TEXTURECOORDINATEBINDING
			*/
			else if (importer_equal_strings(level_name[level], "TEXTURECOORDINATEBINDING") == true)
			{
			
				if (strcmp(word, "{") == 0)
				{
				}
				else if (strcmp(word, "}") == 0)
				{
					level = nlbrack - nrbrack;
				}
				else if (importer_equal_strings(word, "VALUE") == true)
				{
					count = sscanf(next, "%s%n", texture_binding, &width);
					next = next + width;
				}
				else
				{
					bad_num = bad_num + 1;
					printf("Bad data %s\n", word);
				}
			}
			/*
			  TEXTURECOORDINDEX
			*/
			else if (importer_equal_strings(level_name[level], "TEXTURECOORDINDEX") == true)
			{
			
				if (strcmp(word, "[") == 0)
				{
					ivert = 0;
					iface_num = 0;
				}
				else if (strcmp(word, "]") == 0)
				{
					level = nlbrack - nrbrack;
				}
				else
				{
				
					count = sscanf(word, "%d%n", &jval, &width);
					
					if (count > 0)
					{
					
						if (jval == - 1)
						{
							ivert = 0;
						}
						else
						{
						
							if (iface_num < FACE_MAX)
							{
								vertex_tex_uv[0][ivert][iface_num] = texture_temp[0][jval];
								vertex_tex_uv[1][ivert][iface_num] = texture_temp[1][jval];
							}
							
							ivert = ivert + 1;
							
							if (ivert == face_order[iface_num])
							{
								ivert = 0;
								iface_num = iface_num + 1;
							}
						}
						
					}
					else
					{
						bad_num = bad_num + 1;
						printf("Bad data %s\n", word);
					}
					
				}
			}
			/*
			  UKNOTVECTOR
			*/
			else if (importer_equal_strings(level_name[level], "UKNOTVECTOR") == true)
			{
			
				if (strcmp(word, "[") == 0)
				{
					continue;
				}
				else if (strcmp(word, "]") == 0)
				{
					level = nlbrack - nrbrack;
					continue;
				}
				else
				{
					count = sscanf(word, "%d%n", &jval, &width);
				}
			}
			/*
			  VECTOR
			*/
			else if (importer_equal_strings(level_name[level], "VECTOR") == true)
			{
				if (strcmp(word, "[") == 0)
				{
				}
				else if (strcmp(word, "]") == 0)
				{
					level = nlbrack - nrbrack;
				}
				/*
				  (normal) VECTOR
				*/
				else if (importer_equal_strings(level_name[level-1], "NORMAL") == true)
				{
				
					count = sscanf(word, "%f%n", &rval, &width);
					
					if (count > 0)
					{
					
						if (normal_num_temp < ORDER_MAX * FACE_MAX)
						{
							normal_temp[ixyz][normal_num_temp] = rval;
							ixyz = ixyz + 1;
							if (ixyz == 3)
							{
								ixyz = 0;
								normal_num_temp = normal_num_temp + 1;
							}
						}
					}
					else
					{
						bad_num = bad_num + 1;
						printf("NORMAL VECTOR: bad data %s\n", word);
					}
				}
			}
			/*
			  (vertexproperty) VERTEX
			*/
			else if (importer_equal_strings(level_name[level], "VERTEX") == true)
			{
			
				if (importer_equal_strings(level_name[level-1], "VERTEXPROPERTY") == true)
				{
				
					if (strcmp(word, "[") == 0)
					{
						ixyz = 0;
						cor3_num_old = cor3_num;
					}
					else if (strcmp(word, "]") == 0)
					{
						level = nlbrack - nrbrack;
					}
					else
					{
						count = sscanf(word, "%f%n", &rval, &width);
						
						if (count > 0)
						{
						
							if (cor3_num < COR3_MAX)
							{
								cor3[ixyz][cor3_num] = rval;
							}
							ixyz = ixyz + 1;
							if (ixyz == 3)
							{
								ixyz = 0;
								cor3_num = cor3_num + 1;
							}
							
						}
						else
						{
							bad_num = bad_num + 1;
							printf("Bad data %s\n", word);
						}
					}
				}
			}
			/*
			  (indexedtrianglestripset) VERTEXPROPERTY
			*/
			else if (importer_equal_strings(level_name[level], "VERTEXPROPERTY") == true)
			{
			
				if (strcmp(word, "{") == 0)
				{
				}
				else if (strcmp(word, "}") == 0)
				{
					level = nlbrack - nrbrack;
				}
				else if (importer_equal_strings(word, "VERTEX") == true)
				{
				}
				else if (importer_equal_strings(word, "NORMAL") == true)
				{
					ixyz = 0;
				}
				else if (importer_equal_strings(word, "MATERIALBINDING") == true)
				{
					count = sscanf(next, "%s%n", word, &width);
					next = next + width;
				}
				else if (importer_equal_strings(word, "NORMALBINDING") == true)
				{
					count = sscanf(next, "%s%n", word, &width);
					next = next + width;
				}
				else
				{
					bad_num = bad_num + 1;
					printf("Bad data %s\n", word);
				}
			}
			/*
			  VKNOTVECTOR
			*/
			else if (importer_equal_strings(level_name[level], "VKNOTVECTOR") == true)
			{
			
				if (strcmp(word, "[") == 0)
				{
					continue;
				}
				else if (strcmp(word, "]") == 0)
				{
					level = nlbrack - nrbrack;
					continue;
				}
				else
				{
					count = sscanf(word, "%d%n", &jval, &width);
				}
			}
			/*
			  Any other word:
			*/
			else
			{
			}
		}
	}
	/*
	  Reset the transformation matrix to the identity,
	  because, presumably, we've applied it by now.
	*/
	tmat_init(transform_matrix);
	
	nVerts = cor3_num;
	nTris = face_num;
	
	
	verlist = new SSEVertex[nVerts];
	for (unsigned int i = 0; i < nVerts; i++)
	{
		verlist[i] = SSEVertex(cor3[0][i], cor3[1][i], cor3[2][i]);
		verlist[i] *= scalarFactor;
	}
	
	// copy over temporary triangle list:
	trilist = new Triangle[nTris];
	for (unsigned int i = 0; i < nTris; i++)
	{
		Triangle tmp;
		tmp.p[0] = face[0][i];
		tmp.p[1] = face[1][i];
		tmp.p[2] = face[2][i];
		trilist[i] = tmp;
	}
	
	fclose(ivFileStream);
	
	return -1;
}



int ModelInstance::loadSTLA(const char* fileName, float scalarFactor)
{
	FILE* stlFileStream = fopen(fileName, "rb");
	if(!stlFileStream) return -1;
	
	modelFileName = std::string(fileName);
	
	char input[256];
	char token[256];
	int tex_num = 0;
	char* next;
	float r1, r2, r3, r4;
	int face_num = 0;
	int ivert;
	int count;
	float temp[3];
	float cor3[3][COR3_MAX];
	int face[10][COR3_MAX];
	int face_order[COR3_MAX];
	int cor3_num = 0;
	int icor3;
	int i;
	int dump_num = 0;
	int width;
	
	while(fgets(input, 256, stlFileStream))
	{
		tex_num += 1;
		
		for(next = input; *next != '\0' && isspace(*next); next++)
			{}
			
		if(*next == '\0' || *next == '#' || *next == '!' || *next == '$')
		{
			continue;
		}
		
		sscanf(next, "%s%n", token, &width);
		
		next = next + width;
		
		if(importer_equal_strings(token, "facet"))
		{
			// face normal, ignore
			sscanf(next, "%*s %e %e %e", &r1, &r2, &r3);
			
			fgets(input, 256, stlFileStream);
			tex_num += 1;
			
			ivert = 0;
			
			for(;;)
			{
				fgets(input, 256, stlFileStream);
				tex_num += 1;
				
				count = sscanf(input, "%*s %e %e %e", &r1, &r2, &r3);
				
				if(count != 3) break;
				
				temp[0] = r1;
				temp[1] = r2;
				temp[2] = r3;
				
				if(cor3_num < 1000)
				{
					icor3 = importer_rcol_find(cor3, 3, cor3_num, temp);
				}
				else
					icor3 = -1;
					
				if(icor3 == -1)
				{
					icor3 = cor3_num;
					
					if(cor3_num < COR3_MAX)
					{
						for(i = 0; i < 3; ++i)
						{
							cor3[i][cor3_num] = temp[i];
						}
					}
					cor3_num += 1;
				}
				else
				{
					dump_num += 1;
				}
				
				if(ivert < 10 && face_num < COR3_MAX)
				{
					face[ivert][face_num] = icor3;
				}
				
				ivert += 1;
			}
			
			fgets(input, 256, stlFileStream);
			tex_num += 1;
			
			if(ivert > 3)
			{
				printf("Error: triangle mesh required!\n");
				return -1;
			}
			
			
			if(face_num < COR3_MAX)
			{
				face_order[face_num] = ivert;
			}
			
			face_num = face_num + 1;
		}
		else if(importer_equal_strings(token, "color"))
		{
			sscanf(next, "%*s %f %f %f %f", &r1, &r2, &r3, &r4);
		}
		else if(importer_equal_strings(token, "endsolid"))
		{
		}
		else
		{
			printf("\n");
			printf("Error:not supported in stl ascii read!\n");
			return -1;
		}
	}
	
	nVerts = cor3_num;
	nTris = face_num;
	
	
	verlist = (SSEVertex *)malloc(sizeof(SSEVertex) * nVerts);
	for(unsigned int i = 0; i < nVerts; i++)
	{
		verlist[i] = SSEVertex(cor3[0][i], cor3[1][i], cor3[2][i]);
		verlist[i] *= scalarFactor;
	}
	
	// copy over temporary triangle list:
	trilist = new Triangle[nTris];
	for(unsigned int i = 0; i < nTris; i++)
	{
		Triangle tmp;
		tmp.p[0] = face[0][i];
		tmp.p[1] = face[1][i];
		tmp.p[2] = face[2][i];
		trilist[i] = tmp;
	}
	
	fclose(stlFileStream);
	
	return 1;
}


int ModelInstance::loadSTLB(const char* fileName, float scalarFactor)
{
	FILE* stlFileStream = fopen(fileName, "rb");
	if(!stlFileStream)
	{
		return -1;
	}
	
	modelFileName = std::string(fileName);
	
	short attribute = 0;
	char c;
	float cvec[3];
	int icor3;
	int i;
	int iface;
	int ivert;
	int bytes_num = 0;
	int face_num;
	float cor3[3][COR3_MAX];
	int face[3][COR3_MAX];
	int face_order[COR3_MAX];
	int cor3_num = 0;
	int dup_num = 0;
	
	for(i = 0; i < 80; ++i)
	{
		c = (char)fgetc(stlFileStream);
		bytes_num += 1;
	}
	
	face_num = importer_long_int_read(stlFileStream);
	bytes_num += 4;
	
	for(iface = 0; iface < face_num; ++iface)
	{
		face_order[iface] = 3;
		
		
		for(i = 0; i < 3; ++i)
		{
			importer_float_read(stlFileStream);
			bytes_num += 4;
		}
		
		for(ivert = 0; ivert < face_order[iface]; ivert++)
		{
			for(i = 0; i < 3; ++i)
			{
				cvec[i] = importer_float_read(stlFileStream);
				bytes_num += 4;
			}
			
			if(cor3_num < 1000)
			{
				icor3 = importer_rcol_find(cor3, 3, cor3_num, cvec);
			}
			else
				icor3 = -1;
				
			if(icor3 == -1)
			{
				icor3 = cor3_num;
				if(cor3_num < COR3_MAX)
				{
					cor3[0][cor3_num] = cvec[0];
					cor3[1][cor3_num] = cvec[1];
					cor3[2][cor3_num] = cvec[2];
				}
				cor3_num += 1;
			}
			else
				dup_num += 1;
				
			face[ivert][iface] = icor3;
		}
		
		attribute = importer_short_int_read(stlFileStream);
		bytes_num += 2;
	}
	
	
	nVerts = cor3_num;
	nTris = face_num;
	
	verlist = (SSEVertex *)malloc(sizeof(SSEVertex) * nVerts);
	for(unsigned int i = 0; i < nVerts; i++)
	{
		verlist[i] = SSEVertex(cor3[0][i], cor3[1][i], cor3[2][i]);
		verlist[i] *= scalarFactor;
	}
	
	// copy over temporary triangle list:
	trilist = new Triangle[nTris];
	for(unsigned int i = 0; i < nTris; i++)
	{
		Triangle tmp;
		tmp.p[0] = face[0][i];
		tmp.p[1] = face[1][i];
		tmp.p[2] = face[2][i];
		trilist[i] = tmp;
	}
	
	fclose(stlFileStream);
	
	return 1;
}

int ModelInstance::makeBox(float a, float b, float c)
{
	nVerts = 8;
	nTris = 12;
	
	if(verlist) free(verlist);
	if(trilist) delete [] trilist;
	
	verlist = (SSEVertex *)malloc(sizeof(SSEVertex) * nVerts);
	trilist = new Triangle[nTris];
	
	verlist[0] = SSEVertex(0.5f * a, -0.5f * b, 0.5f * c);
	verlist[1] = SSEVertex(0.5f * a, 0.5f * b, 0.5f * c);
	verlist[2] = SSEVertex(-0.5f * a, 0.5f * b, 0.5f * c);
	verlist[3] = SSEVertex(-0.5f * a, -0.5f * b, 0.5f * c);
	verlist[4] = SSEVertex(0.5f * a, -0.5f * b, -0.5f * c);
	verlist[5] = SSEVertex(0.5f * a, 0.5f * b, -0.5f * c);
	verlist[6] = SSEVertex(-0.5f * a, 0.5f * b, -0.5f * c);
	verlist[7] = SSEVertex(-0.5f * a, -0.5f * b, -0.5f * c);
	
	trilist[0].p[0] = 0;
	trilist[0].p[1] = 4;
	trilist[0].p[2] = 1;
	trilist[1].p[0] = 1;
	trilist[1].p[1] = 4;
	trilist[1].p[2] = 5;
	trilist[2].p[0] = 2;
	trilist[2].p[1] = 6;
	trilist[2].p[2] = 3;
	trilist[3].p[0] = 3;
	trilist[3].p[1] = 6;
	trilist[3].p[2] = 7;
	trilist[4].p[0] = 3;
	trilist[4].p[1] = 0;
	trilist[4].p[2] = 2;
	trilist[5].p[0] = 2;
	trilist[5].p[1] = 0;
	trilist[5].p[2] = 1;
	trilist[6].p[0] = 6;
	trilist[6].p[1] = 5;
	trilist[6].p[2] = 7;
	trilist[7].p[0] = 7;
	trilist[7].p[1] = 5;
	trilist[7].p[2] = 4;
	trilist[8].p[0] = 1;
	trilist[8].p[1] = 5;
	trilist[8].p[2] = 2;
	trilist[9].p[0] = 2;
	trilist[9].p[1] = 5;
	trilist[9].p[2] = 6;
	trilist[10].p[0] = 3;
	trilist[10].p[1] = 7;
	trilist[10].p[2] = 0;
	trilist[11].p[0] = 0;
	trilist[11].p[1] = 7;
	trilist[11].p[2] = 4;
	
	return 1;
	
}

int ModelInstance::makeCylinder(float r, float h, int tot)
{
	float phi, phid;
	phid = MATH_PI * 2.0f / tot;
	phi = 0.0f;
	
	nVerts = tot * 2 + 2;
	nTris = tot * 4;
	
	if(verlist) free(verlist);
	if(trilist) delete [] trilist;
	
	verlist = (SSEVertex *)malloc(sizeof(SSEVertex) * nVerts);
	trilist = new Triangle[nTris];
	
	for(int i = 0; i < tot; ++i)
	{
		verlist[i] = SSEVertex(r * cos(phi + phid * i), r * sin(phi + phid * i), h / 2.0f);
		verlist[i + tot] = SSEVertex(r * cos(phi + phid * i), r * sin(phi + phid * i), - h / 2.0f);
	}
	verlist[tot * 2] = SSEVertex(0, 0, h / 2.0f);
	verlist[tot * 2 + 1] = SSEVertex(0, 0, -h / 2.0f);
	
	int triid = 0;
	for(int i = 0; i < tot; ++i)
	{
		Triangle tmp;
		tmp.p[0] = tot * 2;
		tmp.p[1] = i;
		tmp.p[2] = (i == tot - 1) ? 0 : (i + 1);
		trilist[triid] = tmp;
		triid++;
	}
	
	for(int i = 0; i < tot; ++i)
	{
		Triangle tmp;
		tmp.p[0] = tot * 2 + 1;
		tmp.p[1] = (i == tot - 1) ? tot : (i + 1 + tot);
		tmp.p[2] = i + tot;
		trilist[triid] = tmp;
		triid++;
	}
	
	for(int i = 0; i < tot; ++i)
	{
		Triangle tmp;
		int a, b, c, d;
		a = i;
		b = (i == tot - 1) ? 0 : (i + 1);
		c = i + tot;
		d = (i == tot - 1) ? tot : (i + 1 + tot);
		
		tmp.p[0] = b;
		tmp.p[1] = a;
		tmp.p[2] = c;
		trilist[triid] = tmp;
		triid++;
		tmp.p[0] = b;
		tmp.p[1] = c;
		tmp.p[2] = d;
		trilist[triid] = tmp;
		triid++;
	}
	
	return 1;
}

int ModelInstance::makeSphere(float r, int seg, int ring)
{
	nVerts = 2 + ring * seg;
	nTris = seg * 2 + (ring - 1) * seg * 2;
	
	if(verlist) free(verlist);
	if(trilist) delete [] trilist;
	
	verlist = (SSEVertex *)malloc(sizeof(SSEVertex) * nVerts);
	trilist = new Triangle[nTris];
	
	float phi, phid;
	phid = MATH_PI * 2.0f / seg;
	phi = 0.0f;
	
	float theta, thetad;
	thetad = MATH_PI * 1.0f / (ring + 1);
	theta = 0;
	
	for(int i = 0; i < ring; ++i)
	{
		float theta_ = theta + thetad * (i + 1);
		for(int j = 0; j < seg; ++j)
		{
			verlist[i * seg + j] = SSEVertex(r * sin(theta_) * cos(phi + j * phid), r * sin(theta_) * sin(phi + j * phid), r * cos(theta_));
		}
	}
	
	verlist[ring * seg] = SSEVertex(0, 0, r);
	verlist[ring * seg + 1] = SSEVertex(0, 0, -r);
	
	int triid = 0;
	for(int i = 0; i < ring - 1; ++i)
	{
		for(int j = 0; j < seg; ++j)
		{
			Triangle tmp;
			int a, b, c, d;
			a = i * seg + j;
			b = (j == seg - 1) ? (i * seg) : (i * seg + j + 1);
			c = (i + 1) * seg + j;
			d = (j == seg - 1) ? ((i + 1) * seg) : ((i + 1) * seg + j + 1);
			tmp.p[0] = a;
			tmp.p[1] = c;
			tmp.p[2] = b;
			trilist[triid] = tmp;
			triid++;
			tmp.p[0] = b;
			tmp.p[1] = c;
			tmp.p[2] = d;
			trilist[triid] = tmp;
			triid++;
		}
	}
	
	for(int j = 0; j < seg; ++j)
	{
		int a, b;
		a = j;
		b = (j == seg - 1) ? 0 : (j + 1);
		Triangle tmp;
		tmp.p[0] = ring * seg;
		tmp.p[1] = a;
		tmp.p[2] = b;
		trilist[triid] = tmp;
		triid++;
		
		a = (ring - 1) * seg + j;
		b = (j == seg - 1) ? (ring - 1) * seg : ((ring - 1) * seg + j + 1);
		tmp.p[0] = a;
		tmp.p[1] = ring * seg + 1;
		tmp.p[2] = b;
		trilist[triid] = tmp;
		triid++;
	}
	
	return 1;
}

void ModelInstance::saveOBJ(const char* fileName)
{
	FILE* objFileStream = fopen(fileName, "w");
	if(!objFileStream)
	{
		return;
	}
	
	for(unsigned int i = 0; i < nVerts; ++i)
	{
		fprintf(objFileStream, "v %f %f %f\n", verlist[i].x(), verlist[i].y(), verlist[i].z());
	}
	
	for(unsigned int i = 0; i < nTris; ++i)
	{
		fprintf(objFileStream, "f %d %d %d\n", trilist[i].p[0] + 1, trilist[i].p[1] + 1, trilist[i].p[2] + 1);
	}
	
	fclose(objFileStream);
}
